/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package parser.nodes;

import codegen.AbstractDescr;
import codegen.ArrayDescr;
import codegen.CodeGen;
import codegen.ConstDescr;
import codegen.RecordDescr;
import codegen.TypeDescr;
import codegen.VarDescr;
import parser.TokenOr;
import scanner.Token;

public abstract class DereferenceNode extends Node {
	protected Token identifier;
    protected SelectorNode selector;
	protected int lastSize;

	public DereferenceNode(Token identifier, SelectorNode selector) {
		this.identifier = identifier;
		this.selector = selector;
	}

	@Override
	public void compile(CodeGen c) throws CompileError {
		lastSize = 0;
		AbstractDescr descr = c.findDescr(identifier.toString());
		if (descr instanceof ConstDescr) {
			ConstDescr con = (ConstDescr) descr;
			c.outstr("PUSHI, " + con.getValue());
			lastSize = -1;
			return;
		}
		if (!(descr instanceof VarDescr))
			throw new CompileError("Identifier '" + identifier.toString() + "' isn't a variable");
		VarDescr var = (VarDescr) descr;
		if (var == null)
			throw new CompileError("Tried to access unknown variable '" + identifier.toString() + "'");
		boolean isVar = var.isVar();
		c.outstr("PUSHI, " + var.getLevel()); //SL-Register der Variable holen
		c.outstr("GETSL"); //Basisadresse der Variable laden
		c.outstr("PUSHI, " + var.getAddr()); //Relative Adresse auf den Stack legen
		int elems = var.getSize();
		AbstractDescr type = var.getType();
		for (TokenOr<ExpressionNode> sel : selector.getSelectorList()) {
			if (sel.Token() != null) {
				//x . identifier
				AbstractDescr subtype = (type instanceof TypeDescr) ? ((TypeDescr)type).resolve() : null;
				if (!((type instanceof RecordDescr) || (subtype != null && subtype instanceof RecordDescr))) {
					throw new CompileError("Tried to access '" + sel.Token().toString() + "' of variable '" + identifier.toString() + "'");
				}
				RecordDescr record = (RecordDescr) (subtype != null ? subtype : type);
				var = (VarDescr) record.getRecsymbolTable().get(sel.Token().toString());
				type = var.getType();
				if (var == null) {
					throw new CompileError("Unknown field '" + sel.Token().toString() + "' in variable '" + identifier.toString() + "'");
				}
				elems = var.getSize();
				c.outstr("PUSHI, " + var.getAddr());
				c.outstr("ADD");
			} else {
				//x [ Expression ]
				AbstractDescr subtype = (type instanceof TypeDescr) ? ((TypeDescr)type).resolve() : null;
				if (!((type instanceof ArrayDescr)|| (subtype != null && subtype instanceof ArrayDescr))) {
					throw new CompileError("Tried to access array elements of variable '" + identifier.toString() + "'");
				}
				ArrayDescr array = (ArrayDescr) (subtype != null ? subtype : type);
				type = array.GetBasetype();
				elems = type.getSize();
				sel.Node().compile(c);
				c.outstr("PUSHI, " + elems);
				c.outstr("MUL");
				c.outstr("ADD");
			}
		}
		c.outstr("ADD");
		if (isVar)
			c.outstr("CONT, " + elems);
		lastSize = elems;
	}
	
}
